package star.cloud.asme.base.fowable.baseInterface;

import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import star.cloud.asme.base.fowable.VO.*;
import star.cloud.asme.system.user.UserService;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
public class FlowableVOGeneratorService {

    private final RepositoryService repositoryService;

    private final HistoryService historyService;

    private final TaskService taskService;

    private final RuntimeService runtimeService;

    private final UserService userService;


    public ProcessDefinitionVO generateProcessDefinitionVO(String processDefinitionId) {
        ProcessDefinitionVO processDefinitionVO = new ProcessDefinitionVO();
        setProcessDefinitionVO(processDefinitionVO, processDefinitionId);
        return processDefinitionVO;
    }

    public ProcessInstanceVO generateProcessInstanceVO(String processInstanceId) {
        ProcessInstanceVO processInstanceVO = new ProcessInstanceVO();
        setHistoricProcessInstanceVO(processInstanceVO, processInstanceId);
        return processInstanceVO;
    }

    public TaskInstanceVO generateTaskVO(String taskId) {
        TaskInstanceVO taskInstanceVO = new TaskInstanceVO();
        setTaskVO(taskInstanceVO, taskId);
        return taskInstanceVO;
    }

    public void setDeploymentVO(DeploymentVO target, String deploymentId) {
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        target.setId(deploymentId);
        target.setDeploymentId(deployment.getId());
        target.setDeploymentName(deployment.getName());
        target.setDeploymentCategory(deployment.getCategory());
        target.setDeploymentKey(deployment.getKey());
        target.setDeploymentTenantId(Long.valueOf(deployment.getTenantId()));
        target.setDeploymentTime(deployment.getDeploymentTime());
        target.setDeploymentParentDeploymentId(deployment.getParentDeploymentId());
    }

    public void setProcessDefinitionVO(ProcessDefinitionVO target, String processDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        setDeploymentVO(target, processDefinition.getDeploymentId());
        target.setId(processDefinitionId);
        target.setProcessDefinitionId(processDefinition.getId());
        target.setProcessDefinitionKey(processDefinition.getKey());
        target.setProcessDefinitionName(processDefinition.getName());
        target.setProcessDefinitionCategory(processDefinition.getCategory());
        target.setProcessDefinitionVersion(processDefinition.getVersion());
        target.setProcessDefinitionResourceName(processDefinition.getResourceName());
        target.setProcessDefinitionResourceUrl(processDefinition.getResourceName());
        target.setProcessDefinitionDiagramResourceName(processDefinition.getDiagramResourceName());
        target.setProcessDefinitionVersion(processDefinition.getVersion());
        target.setProcessDefinitionDescription(processDefinition.getDescription());
        target.setProcessDefinitionIsSuspended(processDefinition.isSuspended());
        target.setProcessDefinitionDiagramResourceRaw(getProcessDefinitionDiagramResourceRawByProcessDefinitionId(processDefinitionId));
    }

    public void setHistoricProcessInstanceVO(ProcessInstanceVO target, String historicProcessInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicProcessInstanceId).singleResult();
        setProcessDefinitionVO(target, historicProcessInstance.getProcessDefinitionId());
        target.setId(historicProcessInstanceId);
        target.setProcessInstanceId(historicProcessInstance.getId());
        target.setProcessInstanceName(historicProcessInstance.getName());
        target.setProcessInstanceBusinessKey(historicProcessInstance.getBusinessKey());
        target.setProcessInstanceStartTime(historicProcessInstance.getStartTime());
        target.setProcessInstanceEndTime(historicProcessInstance.getEndTime());
        target.setProcessInstanceDuration(historicProcessInstance.getDurationInMillis());
        target.setProcessInstanceStartUserId(historicProcessInstance.getStartUserId());
        target.setProcessInstanceStartUserIdName(userService.getWorkNoAndNameByUsername(historicProcessInstance.getStartUserId()));
        target.setProcessInstanceStartActivityId(historicProcessInstance.getStartActivityId());
        target.setProcessInstanceEndActivityId(historicProcessInstance.getEndActivityId());
        target.setProcessInstanceDescription(historicProcessInstance.getDescription());
        target.setProcessInstanceDeleteReason(historicProcessInstance.getDeleteReason());
        target.setProcessInstanceStatus(getProcessInstanceState(historicProcessInstanceId));
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(historicProcessInstanceId).singleResult();
        if (processInstance != null) {
            target.setProcessInstanceIsSuspended(processInstance.isSuspended());
            target.setProcessInstanceIsEnded(processInstance.isEnded());
        }
    }

    public void setExecution(ExecutionVO target, String executionId) {
        Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
        setHistoricProcessInstanceVO(target, execution.getProcessInstanceId());
        target.setId(executionId);
        target.setExecutionId(execution.getId());
        target.setExecutionName(execution.getName());
        target.setExecutionParentId(execution.getParentId());
        target.setExecutionSuperExecutionId(execution.getSuperExecutionId());
        target.setExecutionRootProcessInstanceId(execution.getRootProcessInstanceId());
        target.setExecutionActivityId(execution.getActivityId());
        target.setExecutionTenantId(execution.getTenantId());
        target.setExecutionDescription(execution.getDescription());
        target.setExecutionReferenceId(execution.getReferenceId());
        target.setExecutionReferenceType(execution.getReferenceType());
        target.setExecutionPropagatedstageInstanceId(execution.getPropagatedStageInstanceId());
        target.setExecutionIsEnded(execution.isEnded());
        target.setExecutionActivityId(execution.getActivityId());
    }

    public void setTaskVO(TaskInstanceVO target, String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        setExecution(target, task.getExecutionId());
        target.setId(taskId);
        target.setTaskId(task.getId());
        target.setTaskOwner(task.getOwner());
        target.setTaskOwnerName(userService.getWorkNoAndNameByUsername(task.getOwner()));
        target.setTaskAssignee(task.getAssignee());
        target.setTaskAssigneeName(userService.getWorkNoAndNameByUsername(task.getAssignee()));
        target.setTaskName(task.getName());
        target.setTaskDescription(task.getDescription());
        target.setTaskState(task.getState());
        target.setTaskCreateTime(task.getCreateTime());
        target.setTaskClaimTime(task.getClaimTime());
        target.setTaskClaimBy(task.getClaimedBy());
        target.setTaskClaimByName(userService.getWorkNoAndNameByUsername(task.getClaimedBy()));
        target.setTaskSuspendedTime(task.getSuspendedTime());
        target.setTaskSuspendedBy(task.getSuspendedBy());
        target.setTaskSuspendedByName(userService.getWorkNoAndNameByUsername(task.getSuspendedBy()));
        target.setTaskSuspensionState(task.isSuspended());
        target.setTaskCreateTime(task.getCreateTime());
        target.setTaskDelegationState(String.valueOf(task.getDelegationState()));
        target.setTaskCategory(task.getCategory());
    }

    public List<TaskInstanceVO> generatetCommentVOList(HistoricTaskInstance task, List<Comment> commentList) {
        List<TaskInstanceVO> taskInstanceVOList = new ArrayList<>();
        commentList.forEach(comment -> {
            TaskInstanceVO taskInstanceVO = new TaskInstanceVO();
            taskInstanceVO.setTaskId(task.getId());
            taskInstanceVO.setTaskName(task.getName());
            taskInstanceVO.setTaskOwner(task.getOwner());
            taskInstanceVO.setTaskOwnerName(userService.getWorkNoAndNameByUsername(task.getOwner()));
            taskInstanceVO.setTaskAssignee(task.getAssignee());
            taskInstanceVO.setTaskAssigneeName(userService.getWorkNoAndNameByUsername(task.getAssignee()));
            taskInstanceVO.setTaskCreateTime(task.getCreateTime());
            taskInstanceVO.setTaskEndTime(task.getEndTime());
            taskInstanceVO.setCommentId(comment.getId());
            taskInstanceVO.setCommentTime(comment.getTime());
            taskInstanceVO.setCommentType(comment.getType());
            taskInstanceVO.setCommentMessage(comment.getFullMessage());
            taskInstanceVO.setCommentUserUsername(comment.getUserId());
            taskInstanceVO.setCommentUserName(userService.getWorkNoAndNameByUsername(comment.getUserId()));
            taskInstanceVOList.add(taskInstanceVO);
        });
        return taskInstanceVOList;
    }

    public int firstResult(IPage<?> iPage) {
        return (int) ((iPage.getCurrent() - 1) * iPage.getSize());
    }

    public int maxResults(IPage<?> iPage) {
        return (int) Math.min(iPage.getCurrent() * iPage.getSize(), iPage.getTotal());
    }

    private String getProcessDefinitionDiagramResourceRawByProcessDefinitionId(String processDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        String resourceName = processDefinition.getResourceName();
        String processDefinitionDiagramResourceRaw = null;
        try (InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName)) {
            processDefinitionDiagramResourceRaw = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
        return processDefinitionDiagramResourceRaw;
    }

    private String getProcessInstanceState(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance.getEndTime() != null) {
            if (historicProcessInstance.getEndActivityId() == null) {
                return "废弃";
            } else {
                return "完成";
            }
        } else {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if (processInstance.isSuspended()) {
                return "暂停";
            } else {
                return "进行";
            }
        }
    }

}
